feat: adaptive zoom intensity based on click spatial density#1666
Open
namearth5005 wants to merge 9 commits intoCapSoftware:mainfrom
Open
feat: adaptive zoom intensity based on click spatial density#1666namearth5005 wants to merge 9 commits intoCapSoftware:mainfrom
namearth5005 wants to merge 9 commits intoCapSoftware:mainfrom
Conversation
…nfigurable fields
…placing hardcoded constants
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Makes auto-zoom smarter by adapting zoom level to click cluster density (refs #1646, builds on #1663, #1664, #1665):
intensity_spatial_scalemin_zoom_amount == max_zoom_amount, behavior is identical to the flatzoom_amountAlgorithm
Config fields added to
AutoZoomConfigmin_zoom_amountmax_zoom_amountintensity_spatial_scaleImplementation
Changed interval tracking from
Vec<(f64, f64)>toVec<(f64, f64, Vec<(f64, f64)>)>to carry click positions through the merge step. Thecompute_zoom_amountfunction computes per-segment zoom from position data.UI changes
Replaced single "Zoom Amount" slider with "Min Zoom" (1.0-3.0) and "Max Zoom" (1.5-4.0) sliders in experimental settings.
Test plan
intensity_tight_cluster_zooms_more— 3 clicks within 0.02 spread → zoom > 2.0intensity_spread_activity_zooms_less— 3 clicks spanning 0.8 → zoom < 1.8intensity_disabled_when_equal— min == max == 1.5 → all segments get exactly 1.5xGreptile Summary
This PR introduces adaptive zoom intensity for auto-zoom-on-clicks: click clusters with tight spatial spread now receive up to 2.5× zoom while spread-out activity scales down to 1.2×, computed via max pairwise distance and linear interpolation against a configurable
intensity_spatial_scale. It also expandsAutoZoomConfigwith dead-zone merging, double-click deduplication, and right-click filtering, and exposes Min/Max Zoom sliders in experimental settings.Key changes:
AutoZoomConfigstruct added inconfiguration.rswith 17 configurable fields and proper serde backward-compatibility annotations on the three new fields.intervalstype widened fromVec<(f64, f64)>toVec<(f64, f64, Vec<(f64, f64)>)>to carry click positions through the merge step, enabling per-segment zoom computation.compute_zoom_amountfunction inrecording.rsimplements the density-based interpolation formula.generate_zoom_segments_from_clicksTauri command now loadsGeneralSettingsStoreto pass the config along.compute_zoom_amountdivides byintensity_spatial_scalewithout guarding against zero; if the scale is manually set to 0.0 and all cluster positions are identical, the result is NaN and propagates toZoomSegment.amount.Confidence Score: 3/5
compute_zoom_amountcan produce a NaN zoom amount that would silently break the rendered segment for manually edited configs.apps/desktop/src-tauri/src/recording.rs(NaN guard incompute_zoom_amount) andapps/desktop/src/routes/(window-chrome)/settings/experimental.tsx(Min/Max Zoom slider ordering validation).Important Files Changed
AutoZoomConfig; newcompute_zoom_amountinner function implements adaptive zoom via max pairwise distance; intervals now carry click positions through the merge step. Two issues found: NaN zoom whenintensity_spatial_scale=0 & max_dist=0, and the dead-zone OR condition can silently merge temporally distant clicks.AutoZoomConfigstruct added with serde camelCase and struct-level#[serde(default)]; the three new fields (min_zoom_amount,max_zoom_amount,intensity_spatial_scale) correctly add explicit per-field#[serde(default = "fn")]for backward-compatibility with existing stored configs. The inconsistency (other fields lack these annotations) is a future-maintenance concern but not an immediate bug.SettingSlidercomponent andhandleConfigChangehelper added; Min Zoom and Max Zoom sliders exposed for the new adaptive zoom feature. Missing constraint enforcement allows min_zoom > max_zoom, which inverts the density-based zoom behavior.generate_zoom_segments_from_clicksTauri command updated to loadGeneralSettingsStoreand passauto_zoom_configto the recording module; error handling uses double-unwrap-or-default pattern which silently falls back to defaults on store read failure.auto_zoom_config: AutoZoomConfigfield added toGeneralSettingsStorewith#[serde(default)];Defaultimpl updated to match. Straightforward and correct.Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD A[generate_zoom_segments_from_clicks\nTauri command] --> B[Load GeneralSettingsStore\nunwrap_or_default] B --> C[generate_zoom_segments_for_project\nmeta + recordings + AutoZoomConfig] C --> D[generate_zoom_segments_from_clicks_impl] D --> E{ignore_right_clicks?} E -- yes --> F[Filter cursor_num != 0] E -- no --> G[Sort clicks by time_ms] F --> G G --> H{double_click_threshold_ms > 0?} H -- yes --> I[Deduplicate rapid same-button clicks] H -- no --> J[Build click_groups] I --> J J --> K{For each unprocessed click:\ntime_and_spatial OR in_dead_zone?} K -- yes --> L[Append to existing group] K -- no --> M[Create new group] L & M --> N[Convert groups → intervals\ncarrying click positions] N --> O[Add movement intervals\npositions = empty vec] O --> P[Sort & Merge intervals\nextend positions on merge] P --> Q[compute_zoom_amount\nper merged segment] Q --> R{max == min?} R -- yes --> S[return zoom_amount\nbackward-compat] R -- no --> T{positions.len < 2?} T -- yes --> U[return max_zoom_amount] T -- no --> V[Compute max pairwise distance] V --> W[t = clamp\nmax_dist / intensity_spatial_scale] W --> X[zoom = max + t × min - max] X --> Y[ZoomSegment\nstart/end/amount] S & U --> YComments Outside Diff (2)
apps/desktop/src/routes/(window-chrome)/settings/experimental.tsx, line 793-810 (link)The "Min Zoom" slider has a range of 1.0–3.0 and "Max Zoom" starts at 1.5–4.0, so a user can drag Min Zoom above Max Zoom (e.g.
minZoomAmount=3.0,maxZoomAmount=1.5). Incompute_zoom_amount, the formula is:When
min > max,(min - max)is positive, sotincreasing with spread increases zoom instead of decreasing it. This silently inverts the intended behavior: tight clusters would receive low zoom (1.5×) while spread activity receives high zoom (3.0×) — the opposite of the feature description. There is no guard in the backend either.Consider adding a UI-level validation that clamps or prevents the inverted state:
Prompt To Fix With AI
apps/desktop/src-tauri/src/recording.rs, line 279-280 (link)intensity_spatial_scaleis zeroWhen
intensity_spatial_scale == 0.0and all positions in a cluster happen to share the same normalized coordinate (somax_dist == 0.0), the division0.0 / 0.0yieldsf64::NaN. In Rust,NaN.clamp(0.0, 1.0)propagatesNaN, sotbecomesNaN, and ultimatelyZoomSegment { amount: NaN, … }is emitted. While the slider for this field is not exposed in the UI,intensity_spatial_scalelives in the serializedAutoZoomConfigand can be set to 0 via the settings JSON file.Add a guard before the division:
Prompt To Fix With AI
Prompt To Fix All With AI
Last reviewed commit: "feat(recording): ada..."